﻿namespace SecutityLib
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class PlayFairCipher : EncodingDecodingTech
    {
        /// <summary>
        /// Encodes the specified plain text.
        /// </summary>
        /// <param name="plainText">The plain text.</param>
        /// <param name="key">The key.</param>
        /// <returns>
        /// cipher text
        /// </returns>
        public override string Encode(string plainText, string key)
        {
            string cipher = string.Empty;
            char[,] keyArr = this.GenerateKey(key);
            plainText = this.FormatString(plainText);

            for (int i = 0; i < plainText.Length; i += 2)
            {
                char one = plainText[i];
                char two = plainText[i + 1];
                int j1 = 0, j2 = 0;
                int k1 = 0, k2 = 0;

                for (int j = 0; j < 5; j++)
                {
                    for (int k = 0; k < 5; k++)
                    {
                        if (keyArr[j, k] == one)
                        {
                            j1 = j;
                            k1 = k;
                        }

                        if (keyArr[j, k] == two)
                        {
                            j2 = j;
                            k2 = k;
                        }
                    }
                }

                if (k1 == k2)
                {
                    cipher += keyArr[(j1 + 1) % 5, k1];
                    cipher += keyArr[(j2 + 1) % 5, k2];
                }
                else if (j1 == j2)
                {
                    cipher += keyArr[j1, (k1 + 1) % 5];
                    cipher += keyArr[j2, (k2 + 1) % 5];
                }
                else
                {
                    cipher += keyArr[j1, k2];
                    cipher += keyArr[j2, k1];
                }
            }

            return cipher;
        }

        /// <summary>
        /// Decodes the specified plain text.
        /// </summary>
        /// <param name="cipherText">the cipher text</param>
        /// <param name="key">The key.</param>
        /// <returns>
        /// cipher text
        /// </returns>
        public override string Decode(string cipherText, string key)
        {
            string plain = string.Empty;
            char[,] keyArr = this.GenerateKey(key);

            cipherText = this.FormatString(cipherText);

            for (int i = 0; i < cipherText.Length; i += 2)
            {
                char one = cipherText[i];
                char two = cipherText[i + 1];
                int j1 = 0, j2 = 0;
                int k1 = 0, k2 = 0;

                for (int j = 0; j < 5; j++)
                {
                    for (int k = 0; k < 5; k++)
                    {
                        if (keyArr[j, k] == one)
                        {
                            j1 = j;
                            k1 = k;
                        }

                        if (keyArr[j, k] == two)
                        {
                            j2 = j;
                            k2 = k;
                        }
                    }
                }

                if (k1 == k2)
                {
                    int temp1 = (j1 - 1) % 5;
                    int temp2 = (j2 - 1) % 5;
                    if (temp1 < 0)
                    {
                        temp1 += 5;
                    }

                    if (temp2 < 0)
                    {
                        temp2 += 5;
                    }

                    plain += keyArr[temp1, k1];
                    plain += keyArr[temp2, k2];
                }
                else if (j1 == j2)
                {
                    int temp1 = (k1 - 1) % 5;
                    int temp2 = (k2 - 1) % 5;
                    if (temp1 < 0)
                    {
                        temp1 += 5;
                    }

                    if (temp2 < 0)
                    {
                        temp2 += 5;
                    }

                    plain += keyArr[j1, temp1];
                    plain += keyArr[j2, temp2];
                }
                else
                {
                    plain += keyArr[j1, k2];
                    plain += keyArr[j2, k1];
                }
            }

            return plain;
        }

        /// <summary>
        /// Generates the key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <returns>key matrix</returns>
        private char[,] GenerateKey(string key)
        {
            for (int i = 0; i < key.Length; i++)
            {
                if (key[i] < 'a' || key[i] > 'z')
                {
                    throw new Exception("key must be alphabetic characters");
                }
            }

            List<char> letters = new List<char>();

            for (int i = 0; i < 26; i++)
            {
                letters.Add((char)('a' + i));
            }

            char[,] keyMatrix = new char[5, 5];
            int cnti = 0;
            int cntj = 0;

            for (int i = 0; i < key.Length; i++)
            {
                if (letters.Count == 0)
                {
                    break;
                }

                if (letters.Contains(key[i]))
                {
                    keyMatrix[cnti, cntj] = key[i];
                    letters.Remove(key[i]);
                    if (key[i] == 'i')
                    {
                        letters.Remove('j');
                    }
                    else if (key[i] == 'j')
                    {
                        letters.Remove('j');
                        keyMatrix[cnti, cntj] = 'i';
                    }

                    cntj++;
                    if (cntj == 5)
                    {
                        cnti++;
                        cntj = 0;
                    }
                }
            }

            for (int i = 0; i < letters.Count; i++)
            {
                keyMatrix[cnti, cntj] = letters[i];
                if (letters[i] == 'i')
                {
                    letters.Remove('j');
                }

                cntj++;
                if (cntj == 5)
                {
                    cnti++;
                    cntj = 0;
                }
            }

            return keyMatrix;
        }

        /// <summary>
        /// Formats the string.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns>the formatted string</returns>
        private string FormatString(string text)
        {
            string formattedStr = string.Empty;

            for (int i = 0; i < text.Length; i++)
            {
                if (i == text.Length - 1)
                {
                    formattedStr += text[i];
                    break;
                }

                if (text[i] != text[i + 1])
                {
                    formattedStr += text[i];
                    formattedStr += text[i + 1];
                    i++;
                }
                else
                {
                    formattedStr += text[i];
                    formattedStr += 'x';
                }
            }

            if (formattedStr.Length % 2 != 0)
            {
                formattedStr += 'x';
            }

            formattedStr.Replace('j', 'i');

            return formattedStr;
        }
    }
}
